import { h, Schema } from "koishi"

export const name = "crates-watcher"

export const inject = ["cron"]

export const Config = Schema.object({
  crates: Schema.array(String),
  channels: Schema.array(String), // platform:channelId
  indexBase: Schema.string().default("https://index.crates.io").role("url"),
})

/** @param {import("koishi").Context} ctx */
export function apply(ctx, config) {
  let lastPoll = -Infinity
  const lastVersions = {}

  function crateIndexPath(name) {
    if (name.length === 1) {
      return `1/${name}`
    } else if (name.length === 2) {
      return `2/${name}`
    } else if (name.length === 3) {
      return `3/${name[0]}/${name}`
    } else {
      return `${name.slice(0, 2)}/${name.slice(2, 4)}/${name}`
    }
  }

  async function fetchLatestVersion(crateName) {
    const path = crateIndexPath(crateName)
    const text = await ctx.http.get(`${config.indexBase}/${path}`, { responseType: "text" })
    const lines = text.trim().split("\n")
    const versions = lines
      .map(line => JSON.parse(line))
      .filter(v => !v.yanked)

    if (!versions.length) return null

    return versions[versions.length - 1]
  }

  async function checkCrates() {
    const now = Date.now()

    for (const crateName of config.crates) {
      try {
        const latestInfo = await fetchLatestVersion(crateName)
        ctx.logger.debug(crateName, latestInfo)
        if (!latestInfo) continue

        const latest = latestInfo.vers

        if (
          lastVersions[crateName] &&
          lastVersions[crateName] !== latest &&
          now > lastPoll
        ) {
          const versionUrl = `https://crates.io/crates/${crateName}/${latest}`
          ctx.broadcast(config.channels, h("i18n:object", {
            path: "lnnbot.crates-watcher.publish",
            data: {
              name: crateName,
              version: latest,
              url: versionUrl,
            },
          }))
        }

        lastVersions[crateName] = latest
      } catch (e) {
        ctx.logger.warn(`Failed to fetch crate ${crateName}:`, e)
      }
    }

    lastPoll = now
  }

  ctx.cron("0 */6 * * *", checkCrates)
  checkCrates()

  ctx.i18n.define("zh-CN", "lnnbot.crates-watcher", {
    publish: "Rust 包 {name} 发布了新版本 {version}：{url}",
  })
}
